import java.net.Socket;
import java.util.Random;
import java.util.Vector;


public class Master extends Machine implements IMaster,IMasterConnection{

	public final static int KEY_BITS = 30;
	Vector<KeySpaceObject> keySpace = null;
	//CircularLinkedList slaveList = null;
	//HashMap<DataObject, ISlave> hashmap;
	UDPConnectionManager connManager = null;
	ServerConnectionManager tcpConnection = null;
	Vector<ClientHandler> cHandlers = null;
	public Thread listenerThread = null;
	
	public synchronized void addToKeySpace(ISlave slave)
	{
		int upper = (int)Math.pow(2, KEY_BITS);
		Random rand = new Random(0);
		boolean found = false;
		int nextPos = 0;
		do{
			found = true;
			nextPos = rand.nextInt(upper); 
			for(int i = 0;i < keySpace.size();i++){
				if(nextPos == keySpace.get(i).getPoint()){
					found = false;
					break;
				}
			}
			
		}while(!found);
		//add the islave at nextpos and put it in the sorted order in keyspace.
		KeySpaceObject ob = new KeySpaceObject(nextPos, slave.getIP(),slave.getID());
		if(keySpace.size() <= 0){			
			keySpace.add(ob);
			return;
		}
		int prev = keySpace.get(0).point;
		for(int i = 1;i < keySpace.size();i++){
			if(nextPos > prev && nextPos < keySpace.get(i).getPoint()){
				keySpace.insertElementAt(ob, i);
				return;
			}
			prev = keySpace.get(i).getPoint();
		}
		keySpace.add(ob);		
	}
	
	public Master(String ip,String id)
	{
		//hashmap = new HashMap<DataObject, ISlave>();
		keySpace = new Vector<KeySpaceObject>();
		cHandlers = new Vector<ClientHandler>();
		initialize(ip,id);
	}
	
	/*@Override
	public ISlave getNextSlave() {
		return slaveList.getNextNode().getSlave();
	}

	@Override
	public ISlave getSlave(DataObject key) {
		return hashmap.get(key);
	}*/
	public void printByteArray(DataObject x)
	{
		for(int i = 0;i < x.length;i++)
			System.out.print(x.getVal()[i] + ",");		
	}
	
	/*public void printAllKeys()
	{
		Iterator<ISlave> it = hashmap.values().iterator();
		Iterator<DataObject> keyit = hashmap.keySet().iterator();
		while(it.hasNext()){
			printByteArray(keyit.next());
			System.out.println(" --> " + it.next().getIP());			
		}
	}*/

	@Override
	
	public void handleMessage(IMessage message) {
		/*if(message.getMessageID() == IMessage.PUTDATA){
			ISlave slave = this.getNextSlave();
			message.setDest(slave);
			connManager.sendData(message, slave);			
		}
		else if(message.getMessageID() == IMessage.GETDATA){
			ISlave slave = this.getSlave(message.getKey());
			IMessage msg = new Message();
			msg.setDest(slave);
			msg.setKey(message.getKey());
			msg.setValue(message.getValue());
			connManager.sendData(msg, slave);
		}*/
		System.out.println("Got Message : " + message);
		if(message.getMessageID() == IMessage.MESSAGE_FROM_SLAVE){
			addToKeySpace(new DummySlave(new String(message.getKey().getVal()) , new String(message.getValue().getVal())));			
		}
		/*else if(message.getMessageID() == IMessage.CLIENT_INQUIRE){
			printAllKeys();
			System.out.println("looking for ");
			printByteArray(message.getKey());			
			ISlave slave = this.getSlave(message.getKey());
			System.out.println(" and got " + slave.getIP());
			IMessage msg = new Message();
			msg.setDest(message.getSource());
			msg.setKey(message.getKey());
			msg.setValue(new DataObject(slave.getIP().getBytes()));
			sendData(msg,msg.getDest());
		}
		else if(message.getMessageID() == IMessage.CLIENT_INQUIRE_FOR_NEWDATA){
			ISlave slave = this.getNextSlave();//(message.getKey());
			IMessage msg = new Message();
			msg.setDest(message.getSource());
			msg.setKey(message.getKey());
			msg.setValue(new DataObject(slave.getIP().getBytes()));
			hashmap.put(message.getKey(), slave);
			sendData(msg,msg.getDest());
		}*/
		
	}

	@Override
	public void initialize(String ip,String id) {
		// TODO Auto-generated method stub
		//connManager = new UDPConnectionManager();
		listenerThread = new MasterListener(this);
		listenerThread.start();
		//slaveList = new CircularLinkedList();
		tcpConnection = new ServerConnectionManager(PORTS.MASTER_LISTENER_PORT);
		//hardcoding the list of slaves for time being
		//DummySlave s1 = new DummySlave(ip, "slave1");
		//DummySlave s2 = new DummySlave(ip, "slave1");
		
		/*slaveList.addAtTail(new Node(s1));
		slaveList.addAtTail(new Node(s2));*/
		//addToKeySpace(s1);
		//addToKeySpace(s2);
		
		while(true){
			Socket s = tcpConnection.connectTo(null);//waiting for a client connection
			//a client has connected to socket s;
			ClientHandler ch = new ClientHandler(this, s);
			System.out.println("got connection from port " + s.getPort());
			cHandlers.add(ch);
			Thread t = new Thread(ch);//ch.run();
			t.start();
			
			System.out.println("\n\n\n After run \n\n\n\n");
			//this.handleMessage(tcpConnection.receiveMessage());		
			
		}
		
	}

	@Override
	public void sendData(IMessage message, IMachine machine) {
		// TODO Auto-generated method stub
		byte[] serializedData = message.serialize();		
		tcpConnection.writeData(machine, serializedData);		
	}
	
	public static void main(String[] args) //ip,id
	{
		Master m = new Master(args[0], args[1]);
	}

	@Override
	public synchronized Vector<KeySpaceObject> getKeySpace() {
		// TODO Auto-generated method stub
		Vector<KeySpaceObject> retVal = new Vector<KeySpaceObject>();
		retVal.addAll(this.keySpace);
		return retVal;
	}

	@Override
	public synchronized void notifyTermination(ClientHandler ch) {
		// TODO Auto-generated method stub
		cHandlers.remove(ch);
		
	}

	
}
